

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GLES/gl.h> /* For NDK*/
#include <android/log.h>
#include "utils.h"
#include "jade.h"

#define DEBUG 1
//#define FLOAT_ARG_FAIL 1

float NORMALS_TABLE[NUMVERTEXNORMALS][3] = {
#include "anorms.h"
};


///////////////////////////////////////////////////////////////////////////////
//OpenGL API

void gluPerspective(GLfloat fovy, GLfloat aspect,
                           GLfloat zNear, GLfloat zFar)
{
    GLfloat xmin, xmax, ymin, ymax;

    ymax = zNear * (GLfloat)tan(fovy * PI / 360);
    ymin = -ymax;
    xmin = ymin * aspect;
    xmax = ymax * aspect;

    glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536),
               (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536),
               (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536));
}

/* Following gluLookAt implementation is adapted from the
 * Mesa 3D Graphics library. http://www.mesa3d.org
 */
void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
	              GLfloat centerx, GLfloat centery, GLfloat centerz,
	              GLfloat upx, GLfloat upy, GLfloat upz)
{
    GLfloat m[16];
    GLfloat x[3], y[3], z[3];
    GLfloat mag;

    /* Make rotation matrix */

    /* Z vector */
    z[0] = eyex - centerx;
    z[1] = eyey - centery;
    z[2] = eyez - centerz;
    mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
    if (mag) {			/* mpichler, 19950515 */
        z[0] /= mag;
        z[1] /= mag;
        z[2] /= mag;
    }

    /* Y vector */
    y[0] = upx;
    y[1] = upy;
    y[2] = upz;

    /* X vector = Y cross Z */
    x[0] = y[1] * z[2] - y[2] * z[1];
    x[1] = -y[0] * z[2] + y[2] * z[0];
    x[2] = y[0] * z[1] - y[1] * z[0];

    /* Recompute Y = Z cross X */
    y[0] = z[1] * x[2] - z[2] * x[1];
    y[1] = -z[0] * x[2] + z[2] * x[0];
    y[2] = z[0] * x[1] - z[1] * x[0];

    /* mpichler, 19950515 */
    /* cross product gives area of parallelogram, which is < 1.0 for
     * non-perpendicular unit-length vectors; so normalize x, y here
     */

    mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
    if (mag) {
        x[0] /= mag;
        x[1] /= mag;
        x[2] /= mag;
    }

    mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
    if (mag) {
        y[0] /= mag;
        y[1] /= mag;
        y[2] /= mag;
    }

#define M(row,col)  m[col*4+row]
    M(0, 0) = x[0];
    M(0, 1) = x[1];
    M(0, 2) = x[2];
    M(0, 3) = 0.0;
    M(1, 0) = y[0];
    M(1, 1) = y[1];
    M(1, 2) = y[2];
    M(1, 3) = 0.0;
    M(2, 0) = z[0];
    M(2, 1) = z[1];
    M(2, 2) = z[2];
    M(2, 3) = 0.0;
    M(3, 0) = 0.0;
    M(3, 1) = 0.0;
    M(3, 2) = 0.0;
    M(3, 3) = 1.0;
#undef M
    {
        int a;
        GLfixed fixedM[16];
        for (a = 0; a < 16; ++a)
            fixedM[a] = (GLfixed)(m[a] * 65536);
        glMultMatrixx(fixedM);
    }

    /* Translate Eye to Origin */
    glTranslatex((GLfixed)(-eyex * 65536),
                 (GLfixed)(-eyey * 65536),
                 (GLfixed)(-eyez * 65536));
}


///////////////////////////////////////////////////////////////////////////////
//Camera

jade_camera_t* jade_camera_create () {

    jade_camera_t *camera;

    camera = (jade_camera_t*)malloc (sizeof (jade_camera_t));
    if (camera == NULL)
        return NULL;

    camera->m_eyex = 0.0f;
    camera->m_eyey = 0.0f;
    camera->m_eyez = 0.0f;
    camera->m_centerx = 0.0f;
    camera->m_centery = 0.0f;
    camera->m_centerz = 0.0f;
    camera->m_upx = 0.0f;
    camera->m_upy = 1.0f;
    camera->m_upz = 0.0f;
    

    return camera;
    
}

void jade_camera_destroy (jade_camera_t* camera) {

    if (camera != NULL)
        FREEANDNULL (camera);

}

void jade_camera_set_eye (jade_camera_t *camera, GLfloat eyex, GLfloat eyey, GLfloat eyez) {

#ifdef FLOAT_ARG_FAIL
    camera->m_eyex = 0.0f;
    camera->m_eyey = 10.0f;
    camera->m_eyez = 30.0f;

#else

    camera->m_eyex = eyex;
    camera->m_eyey = eyey;
    camera->m_eyez = eyez;

#endif

}

void jade_camera_set_center (jade_camera_t *camera, GLfloat centerx, GLfloat centery, GLfloat centerz) {

    camera->m_centerx = centerx;
    camera->m_centery = centery;
    camera->m_centerz = centerz;
}

void jade_camera_set_up (jade_camera_t *camera, GLfloat upx, GLfloat upy, GLfloat upz) {

    camera->m_upx = upx;
    camera->m_upy = upy;
    camera->m_upz = upz;
}


void jade_camera_glulookat (jade_camera_t *camera) {

    gluLookAt(camera->m_eyex, camera->m_eyey, camera->m_eyez,
	          camera->m_centerx, camera->m_centery, camera->m_centerz,
	          camera->m_upx, camera->m_upy, camera->m_upz);

/*
    LOGD ("eyex = f%, eyey = %f, eyez = %f.\n", camera->m_eyex, camera->m_eyey, camera->m_eyez);
    gluLookAt(60.0f, 15.0f, 60.0f, 0.0f, 0.0f, 0.0f,
	          0.0f, 1.0f, 0.0f);
*/
/*
    gluLookAt(0.0f, 0.0f, 30.0f, 0.0f, 0.0f, 0.0f,
	          0.0f, 1.0f, 0.0f);
*/
}

///////////////////////////////////////////////////////////////////////////////
//World

jade_world_t* 
jade_world_create (int width, int height, 
        GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha){

    jade_world_t *world;

    world = (jade_world_t*)malloc (sizeof (jade_world_t));
    if (world == NULL)
        return NULL;

    world->m_width = width;
    world->m_height = height;
    world->m_bgred = red;
    world->m_bggreen = green;
    world->m_bgblue = blue;
    world->m_bgalpha = alpha;
    world->m_fovy = 15.0f;
    world->m_znear = 0.5f;
    world->m_zfar = 150.0f;
    world->m_camera = NULL;


    glViewport(0, 0, width, height);
    //fovy, aspect, zNear, zFar
    gluPerspective(world->m_fovy, (float)width / height, world->m_znear, world->m_zfar);
    //gluPerspective(15.0f, (float)width / height, 0.5f, 150.0f);


    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity();

    LOGD ("jade_world_create: red = %f, green = %f, blue =%f, alpha = %f.\n", red, green, blue, alpha);

#ifdef FLOAT_ARG_FAIL
    glClearColor(255.0f, 0.0f, 0.0f, 0.0f);
#else
    glClearColor(red, green, blue, alpha);
#endif
    
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glShadeModel(GL_SMOOTH);


    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //GL_FASTEST GL_NICEST

    return world;
}

void jade_world_destroy (jade_world_t *world) {
    
    if (world->m_camera != NULL)
        FREEANDNULL (world->m_camera);

    FREEANDNULL (world);   
}

void jade_world_set_camera (jade_world_t *world, jade_camera_t *camera) {
    
    world->m_camera = camera;

}

void jade_world_prepare_render(jade_world_t* world)
{

///    glViewport(0, 0, width, height);
/*
    glClearColorx((GLfixed)(0.1f * 65536),
                  (GLfixed)(0.2f * 65536),
                  (GLfixed)(0.3f * 65536), 0x10000);
*/
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(world->m_fovy, (float)world->m_width / world->m_height, world->m_znear, world->m_zfar);
    //gluPerspective(15, (float)world->m_width / world->m_height, 0.5f, 150);
    //gluPerspective(60, (float)width / height, 1.0f, 1000);
    //gluPerspective(45, (float)width / height, 0.5f, 150);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

/*
    gluLookAt(0.0f, 0.0f, 30.0f, 0.0f, 0.0f, 0.0f,
	          0.0f, 1.0f, 0.0f);
*/
    jade_camera_glulookat (world->m_camera);
}



///////////////////////////////////////////////////////////////////////////////
//jade mesh API


jade_mesh_t *
jade_mesh_create () {
//jade_mesh_create (GLfloat *vertices, GLfloat *normals, GLfloat *uvs, GLint triangle_num) {

    jade_mesh_t *mesh;

    mesh = (jade_mesh_t*)malloc (sizeof (jade_mesh_t));
    if (mesh == NULL)
        return NULL;

    mesh->m_vertices = NULL;
    mesh->m_normals = NULL;
    mesh->m_uvs = NULL;
    mesh->m_colors = NULL;
    mesh->m_indices = NULL;
    mesh->m_position = NULL;
    mesh->m_rotate = NULL;
    mesh->m_scale = NULL;
    mesh->m_textureId = -1;
    mesh->m_triangleNums = 0;
    mesh->m_enabled = GL_FALSE;
    mesh->m_texureoffset = NULL;

    return mesh;
}

void jade_mesh_destroy (jade_mesh_t *mesh) {

    if (mesh == NULL) {
        return;
    }

    if (mesh->m_vertices != NULL) {
        FREEANDNULL (mesh->m_vertices);
    }

    if (mesh->m_normals != NULL) {

        FREEANDNULL (mesh->m_normals);
    }


    if (mesh->m_uvs != NULL) {

        FREEANDNULL (mesh->m_uvs);
    }


    if (mesh->m_colors != NULL) {

        FREEANDNULL (mesh->m_colors);
    }

    if (mesh->m_indices != NULL) {

        FREEANDNULL (mesh->m_indices);
    }

    if (mesh->m_position != NULL) {

        FREEANDNULL (mesh->m_position);
    }

    if (mesh->m_rotate != NULL) {

        FREEANDNULL (mesh->m_rotate);
    }


    if (mesh->m_texureoffset != NULL) {

        FREEANDNULL (mesh->m_texureoffset); 
    }


    if (mesh->m_scale != NULL) {

        FREEANDNULL (mesh->m_scale);
    }


    if (mesh != NULL)
        FREEANDNULL (mesh);



    return;
}


void jade_mesh_set_triangle_num (jade_mesh_t *mesh, GLint num) {

     mesh->m_triangleNums = num;
}

void jade_mesh_set_vertices (jade_mesh_t *mesh, GLfloat *vertices, int size) {

    FREEANDNULL(mesh->m_vertices);
    mesh->m_vertices = (GLfloat *) malloc(size);
    memcpy(mesh->m_vertices, vertices, size);

    mesh->m_enabled = GL_TRUE;
}

void jade_mesh_set_normals (jade_mesh_t *mesh, GLfloat *normals, int size) {

    FREEANDNULL(mesh->m_normals);
    mesh->m_normals = (GLfloat *) malloc(size);
    memcpy(mesh->m_normals, normals, size);
}

void jade_mesh_set_uvs (jade_mesh_t *mesh, GLfloat *uvs, int size) {

    FREEANDNULL(mesh->m_uvs);
    mesh->m_uvs = (GLfloat *) malloc(size);
    memcpy(mesh->m_uvs, uvs, size);
}

void jade_mesh_set_colors (jade_mesh_t *mesh, GLubyte *colors, int size) {

    FREEANDNULL(mesh->m_colors);
    mesh->m_colors = (GLubyte *) malloc(size);
    memcpy(mesh->m_colors, colors, size);
}

void 
jade_mesh_set_indices (jade_mesh_t *mesh, GLshort *indices, int size) {

    FREEANDNULL(mesh->m_indices);
    mesh->m_indices = (GLshort *) malloc(size);
    memcpy(mesh->m_indices, indices, size);
}



void jade_mesh_set_texture_id (jade_mesh_t *mesh, GLint textureId) {

    mesh->m_textureId = textureId;

    return;

}

void jade_mesh_set_position (jade_mesh_t *mesh, GLfloat x, GLfloat y, GLfloat z) {


    LOGD ("jade_mesh_set_position: x = %f, y = %f, z = %f.\n", x, y, z);

    if (mesh->m_position == NULL) {
        mesh->m_position = (GLfloat *) malloc(3 * sizeof(GLfloat));
    }       

    mesh->m_position[0] = x;
    mesh->m_position[1] = y;
    mesh->m_position[2] = z;

    return;
}


void jade_mesh_set_rotate(jade_mesh_t *mesh, GLfloat x, GLfloat y, GLfloat z) {

    if (mesh->m_rotate == NULL) {
        mesh->m_rotate = (GLfloat *) malloc(3 * sizeof(GLfloat));
    }

    mesh->m_rotate[0] = x;
    mesh->m_rotate[1] = y;
    mesh->m_rotate[2] = z;
/*

    m_rotate[0] = a;
    m_rotate[1] = b;
    m_rotate[2] = c;
*/

/*
    m_rotate[0] = 10.0f;
    m_rotate[1] = 10.0f;
    m_rotate[2] = 10.0f;
*/


    LOGD("m_rotate[0] = %f, r[1] = %f, r[2] = %f.\n", mesh->m_rotate[0], mesh->m_rotate[1], mesh->m_rotate[2]);
}

void jade_mesh_set_scale (jade_mesh_t *mesh, GLfloat x, GLfloat y, GLfloat z) {
    if (mesh->m_scale == NULL) {
        mesh->m_scale = (GLfloat *) malloc(3 * sizeof(GLfloat));
    }

    mesh->m_scale[0] = x;
    mesh->m_scale[1] = y;
    mesh->m_scale[2] = z;
}

void jade_mesh_set_translate (jade_mesh_t *mesh, GLfloat x, GLfloat y, GLfloat z) {
    if (mesh->m_scale == NULL) {
        mesh->m_scale = (GLfloat *) malloc(3 * sizeof(GLfloat));
    }

    mesh->m_scale[0] = x;
    mesh->m_scale[1] = y;
    mesh->m_scale[2] = z;
}



void jade_mesh_set_texture_offset(jade_mesh_t *mesh, GLfloat x, GLfloat y, GLfloat z) {


    LOGD("jade_mesh_set_texture_offset: x = %f, y = %f, z = %f.\n", x, y, z);

    if (mesh->m_texureoffset == NULL) {
        mesh->m_texureoffset = (GLfloat *) malloc(3 * sizeof(GLfloat));
    }

    mesh->m_texureoffset[0] = x;
    //m_texureoffset[1] = -0.25;
    mesh->m_texureoffset[1] = y;
    mesh->m_texureoffset[2] = z;

    LOGD("m_textureoffset[0] = %f, t[1] = %f, t[2] = %f.\n", 
            mesh->m_texureoffset[0], mesh->m_texureoffset[1], mesh->m_texureoffset[2]);
}


static void init_glcmds (jade_mesh_t *mesh) {

    if (mesh->m_vertices != NULL)
        glEnableClientState(GL_VERTEX_ARRAY);

    if (mesh->m_normals != NULL)
        glEnableClientState(GL_NORMAL_ARRAY);

    if (mesh->m_uvs != NULL) {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnable(GL_TEXTURE_2D);
    } else {
        glDisable(GL_TEXTURE_2D);
    }
if (mesh->m_colors != NULL)
        glEnableClientState(GL_COLOR_ARRAY);
}




void jade_mesh_render (jade_mesh_t *mesh) {

    if (!mesh->m_enabled)
        return;

///    prepare_frame ();


    //save current matrix
    glPushMatrix();

    //enable or disable gl command status
    init_glcmds(mesh);

    if (mesh->m_position != NULL) {
        glTranslatef(mesh->m_position[0], mesh->m_position[1], mesh->m_position[2]);
    }

    if (mesh->m_rotate != NULL) {

        
        //LOGD("m_rotate[0] = %f, r[1] = %f, r[2] = %f.\n", m_rotate[0], m_rotate[1], m_rotate[2]);

        //ratate x axis
        if (mesh->m_rotate[0] != 0.0f)
            glRotatef(mesh->m_rotate[0], 1.0f, 0.0f, 0.0f);
        //ratate y axis
        if (mesh->m_rotate[1] != 0.0f)
            glRotatef(mesh->m_rotate[1], 0.0f, 1.0f, 0.0f);
        //ratate z axis
        if (mesh->m_rotate[2] != 0.0f)
            glRotatef(mesh->m_rotate[2], 0.0f, 0.0f, 1.0f);


        //glRotatef(30.0f, 0.0f, 1.0f, 0.0f);
    }

    if (mesh->m_scale != NULL)
        glScalef(mesh->m_scale[0], mesh->m_scale[1], mesh->m_scale[2]);


    /* Begin: Add for moving texture */
    if (mesh->m_texureoffset != NULL) {

        glMatrixMode(GL_TEXTURE);
        glPushMatrix();
        glLoadIdentity();

        if (mesh->m_texureoffset[0] != 0.0f)
            glTranslatef(mesh->m_texureoffset[0], 0.0f, 0.0f);

        if (mesh->m_texureoffset[1] != 0.0f)
            glTranslatef(0.0f, mesh->m_texureoffset[1], 0.0f);

        if (mesh->m_texureoffset[2] != 0.0f)
            glTranslatef(0.0f, 0.0f, mesh->m_texureoffset[2]);

    }
    /* End: Add for moving texture */


    if (mesh->m_vertices != NULL) {
        glVertexPointer(3, GL_FLOAT, 0, mesh->m_vertices);
    }
    else {
#ifdef DEBUG
        LOGD("No vertices, exit render!\n");
#endif
        return;
    }

    if (mesh->m_normals != NULL)
        glNormalPointer(GL_FLOAT, 0, mesh->m_normals);

    if (mesh->m_uvs != NULL && mesh->m_textureId != -1) {
        glBindTexture(GL_TEXTURE_2D, mesh->m_textureId);
        glTexCoordPointer(2, GL_FLOAT, 0, mesh->m_uvs);
    } else {
        glDisable(GL_TEXTURE_2D);
        if ((mesh->m_uvs != NULL && mesh->m_textureId == -1) ||
                (mesh->m_uvs == NULL && mesh->m_textureId != -1) ) {
#ifdef DEBUG
            //LOGD("Only have uvs or texture id!\n");
#endif
        }
    }

    if (mesh->m_colors != NULL)
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh->m_colors);

    if (mesh->m_indices != NULL) {
        glDrawElements(GL_TRIANGLES, mesh->m_triangleNums * 3, GL_UNSIGNED_SHORT, mesh->m_indices);
    }
    else {
        glDrawArrays(GL_TRIANGLES, 0, mesh->m_triangleNums * 3);
    }

    //restore matrix
    glPopMatrix();


}


///////////////////////////////////////////////////////////////////////////////
/// Jade model API


void jade_model_set_mesh_count (jade_model_t *model, int meshCount) {

    int i = 0;

    if (model->m_meshes != NULL) {
        for (; i < model->m_meshCount; i++)
            jade_mesh_destroy (model->m_meshes[i]);

        FREEANDNULL (model->m_meshes);
    }    


    if (meshCount > 0) {
        
        model->m_meshCount = meshCount; 
        model->m_meshes = (jade_mesh_t**)malloc (sizeof (jade_mesh_t*) * meshCount);
        for (i = 0; i < meshCount; i++) {
            model->m_meshes[i] = jade_mesh_create ();
            //model->m_meshes[i] = (jade_mesh_t *)malloc (sizeof (jade_mesh_t));
        }

    }   

}



jade_model_t *jade_model_create (int mesh_count) {

    jade_model_t *model = NULL;


    model = (jade_model_t*)malloc (sizeof (jade_model_t));
    if (model == NULL)
        return NULL;

    model->m_meshes = NULL;
    model->m_meshCount = 0;
 
    if (mesh_count > 0)
        jade_model_set_mesh_count (model, mesh_count);

    return model;
}

void jade_model_destroy (jade_model_t *model) {

    int i = 0;


    if (model->m_meshCount > 0) {

        for (; i < model->m_meshCount; i++)
            jade_mesh_destroy (model->m_meshes[i]);
    
        FREEANDNULL (model->m_meshes);
        FREEANDNULL (model);
    }
}

void 
jade_model_set_vertices (jade_model_t *model, GLfloat *vertices, int size, int meshIndex) {

 
    jade_mesh_set_vertices(model->m_meshes[meshIndex], vertices, size);

    return;
}

void 
jade_model_set_normals (jade_model_t *model, GLfloat *normals, int size, int meshIndex) {

    jade_mesh_set_normals (model->m_meshes[meshIndex], normals, size);
}

void 
jade_model_set_uvs (jade_model_t *model, GLfloat *uvs, int size, int meshIndex) {

    jade_mesh_set_uvs (model->m_meshes[meshIndex], uvs, size);
}

void 
jade_model_set_colors(jade_model_t *model, GLubyte *colors, int size, int meshIndex) {

    jade_mesh_set_colors(model->m_meshes[meshIndex], colors, size);
}

void
jade_model_set_indices (jade_model_t *model, GLshort *indices, int size, int meshIndex) {

    jade_mesh_set_indices (model->m_meshes[meshIndex], indices, size);
}

void 
jade_model_set_triangle_num(jade_model_t *model, GLint triangleNums, int meshIndex) {

    jade_mesh_set_triangle_num(model->m_meshes[meshIndex], triangleNums);
}


void jade_model_render (jade_model_t *model) {

    int i = 0;

    for (i = 0; i < model->m_meshCount; i++) {
        jade_mesh_render (model->m_meshes[i]);
    }

    return;
}




///////////////////////////////////////////////////////////////////////////////
//jade md2 model API

int jade_model_md2_get_animation_count(jade_model_md2_t *model) {

    int i, j, pos;
    int count;
    int lastId;
    char name[16], last[16];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            strcpy (last, name);
            count++;
        }
    }

    return count;
}


const char* 
jade_model_md2_get_animation_name (jade_model_md2_t *model, int animation) {

    int i, j, pos;
    int count;
    int lastId;
    static char last[32];
    char name[32];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            if (count == animation)
                return last;

            strcpy (last, name);
            count++;
        }
    }

    return 0;
}


void 
jade_model_md2_get_animation_frames (jade_model_md2_t *model, 
        int animation, unsigned int *startFrame, unsigned int *endFrame) {

    int i, j, pos;
    int count, numFrames, frameCount;
    int lastId;
    char name[16], last[16];

    strcpy (last, model->m_frames[0].name);
    pos = strlen (last) - 1;
    j = 0;
    while (last[pos] >= '0' && last[pos] <= '9' && j < 2) {
        pos--;
        j++;
    }
    last[pos + 1] = '\0';

    lastId = 0;
    count = 0;
    numFrames = 0;
    frameCount = 0;

    for (i = 0; i <= model->m_header.numFrames; i++) {
        if (i == model->m_header.numFrames)
            strcpy (name, ""); // some kind of a sentinel
        else
            strcpy (name, model->m_frames[i].name);
        pos = strlen (name) - 1;
        j = 0;
        while (name[pos] >= '0' && name[pos] <= '9' && j < 2) {
            pos--;
            j++;
        }
        name[pos + 1] = '\0';

        if (strcmp (last, name)) {
            strcpy (last, name);

            if (count == animation) {
                *startFrame = frameCount - numFrames;
                *endFrame = frameCount - 1;
                return;
            }

            count++;
            numFrames = 0;
        }
        frameCount++;
        numFrames++;
    }

    *startFrame = *endFrame = 0;
}



void jade_model_md2_set_action_index (jade_model_md2_t *model, int index) {

    if (index < model->m_actionCount) {
        model->m_actionIdx = index;
        model->m_frameIdx = model->m_actions[model->m_actionIdx].min_idx;
        model->mesh->m_enabled = GL_TRUE;
    } else {
        model->mesh->m_enabled = GL_FALSE;
        model->m_actionIdx = -1;
    }
}



jade_model_md2_t *
jade_model_md2_create (char* filename) {

    FILE *file;
    byte buffer[MD2_MAX_FRAMESIZE];
    int i;

    jade_model_md2_t *model;

    __android_log_print(ANDROID_LOG_INFO, "AndMii", "test i:%d\n", i);

    file = fopen (filename, "rb");
    if (!file)
        return NULL;

    model = malloc (sizeof (jade_model_md2_t)); 
    if (model == NULL)
        return NULL;

    /* initialize model and read header */
    fread (&model->m_header, sizeof (jade_model_md2_header_t), 1, file);

    
    LOGD ("skinWidth:\t\t%d\n", model->m_header.skinWidth);
    LOGD ("skinHeight:\t\t%d\n", model->m_header.skinHeight);

#if DEBUG
    LOGD ("magic:\t\t%d\n", model->m_header.magic);
    LOGD ("version:\t\t%d\n", model->m_header.version);
    LOGD ("skinWidth:\t\t%d\n", model->m_header.skinWidth);
    LOGD ("skinHeight:\t\t%d\n", model->m_header.skinHeight);
    LOGD ("frameSize:\t\t%d\n", model->m_header.frameSize);
    LOGD ("numSkins:\t\t%d\n", model->m_header.numSkins);
    LOGD ("numVertices:\t\t%d\n", model->m_header.numVertices);
    LOGD ("numTexCoords:\t\t%d\n", model->m_header.numTexCoords);
    LOGD ("numTriangles:\t\t%d\n", model->m_header.numTriangles);
    LOGD ("numGlCommands:\t\t%d\n", model->m_header.numGlCommands);
    LOGD ("numFrames:\t\t%d\n", model->m_header.numFrames);
    LOGD ("offsetSkins:\t\t%d\n", model->m_header.offsetSkins);
    LOGD ("offsetTexCoords:\t%d\n", model->m_header.offsetTexCoords);
    LOGD ("offsetTriangles:\t%d\n", model->m_header.offsetTriangles);
    LOGD ("offsetFrames:\t\t%d\n", model->m_header.offsetFrames);
    LOGD ("offsetGlCommands:\t%d\n", model->m_header.offsetGlCommands);
    LOGD ("offsetEnd:\t\t%d\n", model->m_header.offsetEnd);
#endif

    __android_log_print(ANDROID_LOG_INFO, "AndMii", "magic:%d\n", model->m_header.magic);

    if (model->m_header.magic != (int) (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')) {
        fclose(file);
        FREEANDNULL (model);
        return NULL;
    }

    /* read skins */
    fseek (file, model->m_header.offsetSkins, SEEK_SET);
    if (model->m_header.numSkins > 0) {

        model->m_skins = (jade_model_md2_skin_t *) malloc(sizeof(jade_model_md2_skin_t) * model->m_header.numSkins);
        if (!model->m_skins) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numSkins; i++)
            fread (&model->m_skins[i], sizeof(jade_model_md2_skin_t), 1, file);
    }

    /* read texture coordinates */
    fseek (file, model->m_header.offsetTexCoords, SEEK_SET);
    if (model->m_header.numTexCoords > 0) {
        model->m_texCoords = (jade_model_md2_textureCoordinate_t *) 
                malloc (sizeof (jade_model_md2_textureCoordinate_t) * model->m_header.numTexCoords);
        if (!model->m_texCoords) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numTexCoords; i++)
            fread (&model->m_texCoords[i], sizeof (jade_model_md2_textureCoordinate_t), 1, file);
    }

    /* read triangles */
    fseek (file, model->m_header.offsetTriangles, SEEK_SET);
    if (model->m_header.numTriangles > 0) {

        model->m_triangles = (jade_model_md2_triangle_t *) 
                malloc(sizeof(jade_model_md2_triangle_t) * model->m_header.numTriangles);

        if (!model->m_triangles) {

            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numTriangles; i++)
            fread (&model->m_triangles[i], sizeof (jade_model_md2_triangle_t), 1, file);
    }

    /* read alias frames */
    fseek (file, model->m_header.offsetFrames, SEEK_SET);
    if (model->m_header.numFrames > 0) {
        model->m_frames = (jade_model_md2_frame_t *) 
                malloc (sizeof (jade_model_md2_frame_t) * model->m_header.numFrames);

        if (!model->m_frames) {
            FREEANDNULL (model);
            fclose (file);
            return NULL;
        }

        for (i = 0; i < model->m_header.numFrames; i++) {
            jade_model_md2_alias_frame_t *frame = (jade_model_md2_alias_frame_t *) buffer;
            int j;

            model->m_frames[i].vertices = (jade_model_md2_triangleVertex_t *) 
                malloc (sizeof (jade_model_md2_triangleVertex_t) * model->m_header.numVertices);

            if (!model->m_frames[i].vertices) {
                FREEANDNULL (model);
                fclose (file);
                return NULL;
            }

            fread (frame, 1, model->m_header.frameSize, file);
            strcpy (model->m_frames[i].name, frame->name);
            for (j = 0; j < model->m_header.numVertices; j++) {
                model->m_frames[i].vertices[j].vertex[0] = (float) ((int) frame->alias_vertices[j].vertex[0]) * frame->scale[0] + frame->translate[0];
                model->m_frames[i].vertices[j].vertex[2] = -1* ((float) ((int) frame->alias_vertices[j].vertex[1]) * frame->scale[1] + frame->translate[1]);
                model->m_frames[i].vertices[j].vertex[1] = (float) ((int) frame->alias_vertices[j].vertex[2]) * frame->scale[2] + frame->translate[2];

                model->m_frames[i].vertices[j].normal[0] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][0];
                model->m_frames[i].vertices[j].normal[1] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][1];
                model->m_frames[i].vertices[j].normal[2] = NORMALS_TABLE[frame->alias_vertices[j].lightNormalIndex][2];
                //model->frames[i].vertices[j].lightNormalIndex = frame->alias_vertices[j].lightNormalIndex;
/*                
                LOGD("frame[%d].vertices[%d].vertex[0]=%f, vertex[1] =%f, vertex[2] = %f\n", i, j, 
                    model->m_frames[i].vertices[j].vertex[0], 
                    model->m_frames[i].vertices[j].vertex[1],
                    model->m_frames[i].vertices[j].vertex[2]);
*/
            }
        }
    }

    //ignore gl commands reading

    fclose (file);

    model->mesh = jade_mesh_create ();
    jade_mesh_set_triangle_num (model->mesh, model->m_header.numTriangles);

    model->m_actionCount = jade_model_md2_get_animation_count (model);

#if DEBUG
    LOGD("getAnimationCount:\t%d\n", model->m_actionCount);
#endif

    model->m_actions = (jade_model_md2_action_t *) malloc(model->m_actionCount * sizeof(jade_model_md2_action_t));
    for (i = 0; i < (int) model->m_actionCount; i++) {
        model->m_actions[i].index = i;
        strcpy (model->m_actions[i].name, jade_model_md2_get_animation_name(model, i));
        jade_model_md2_get_animation_frames(model, i, &(model->m_actions[i].min_idx), &(model->m_actions[i].max_idx ));
#if DEBUG
        LOGD("m_actions[%d]:\t\t%s [%d, %d]\n", i, model->m_actions[i].name, 
                model->m_actions[i].min_idx, model->m_actions[i].max_idx);
#endif
    }

    return model;

}


void jade_model_md2_destroy (jade_model_md2_t *model) {


    FREEANDNULL (model->m_skins);
    FREEANDNULL (model->m_texCoords);
    FREEANDNULL (model->m_triangles);
    FREEANDNULL (model->m_frames);
    FREEANDNULL (model->m_actions);


    if (model->mesh != NULL)
        jade_mesh_destroy (model->mesh);


    FREEANDNULL (model);

    return;
}

void jade_model_md2_prepare_frame (jade_model_md2_t *model) {


    if (model->m_actionIdx == -1)
        return;

    float *uvs = (float *) malloc(model->m_header.numTriangles * 6 * sizeof(float));
    float *vertices = (float *) malloc(model->m_header.numTriangles * 9 * sizeof(float));
    float *normals = (float *) malloc(model->m_header.numTriangles * 9 * sizeof(float));
    int i, j, uvIdx = 0, vertexIdx = 0, normalIdx = 0;

    jade_model_md2_frame_t *f = &model->m_frames[model->m_frameIdx];

    for (i = 0; i < model->m_header.numTriangles; i++) {
        jade_model_md2_triangle_t *t = &model->m_triangles[i];
        for (j = 0; j < 3; j++) {
            //set uvs
            uvs[uvIdx++] = (float) model->m_texCoords[t->textureIndices[j]].s / (float) model->m_header.skinWidth;
            uvs[uvIdx++] = 1.0f - (float) model->m_texCoords[t->textureIndices[j]].t / (float) model->m_header.skinHeight;
            //set vertices & normals
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[0];
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[1];
            vertices[vertexIdx++] = f->vertices[t->vertexIndices[j]].vertex[2];

            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[0];
            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[2];
            normals[normalIdx++] = f->vertices[t->vertexIndices[j]].normal[1];
        }
    }

    jade_mesh_set_vertices(model->mesh, vertices, model->m_header.numTriangles * 9 * sizeof(float));
    jade_mesh_set_normals (model->mesh, normals, model->m_header.numTriangles * 9 * sizeof(float));
    jade_mesh_set_uvs (model->mesh, uvs, model->m_header.numTriangles * 6 * sizeof(float));

    FREEANDNULL(vertices);
    FREEANDNULL(normals);
    FREEANDNULL(uvs);

    model->m_frameIdx++;

    if (model->m_frameIdx > model->m_actions[model->m_actionIdx].max_idx)
        model->m_frameIdx = model->m_actions[model->m_actionIdx].min_idx;


    return;
}

void jade_model_md2_render (jade_model_md2_t *model) {

    if (model->mesh != NULL)
        jade_mesh_render (model->mesh);

    return; 
}


void jade_model_md2_set_rotate (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    LOGD ("mix=%d, miy=%d, miz=%d\n", x, y, z);
    //LOGD ("mfx=%d, mfy=%d, mfz=%d\n", x, y, z);
    //LOGD ("gx=%f, gy=%f, gz=%f\n", (GLfloat)x, (GLfloat)y, (GLfloat)z);
    //setRotate ((GLfloat)10.0f, (GLfloat)10.0f, (GLfloat)10.0f);
    //setRotate ((float)x, (float)y, (float)z);
    jade_mesh_set_rotate (model->mesh, x, y, z);
    //setRotate (x, y, z);
    //LOGD ("mfx=%f, mfy=%f, mfz=%f\n", (float)x, (float)y, (float)z);

}

void jade_model_md2_set_texture_offset(jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    LOGD ("jade_model_md2_set_texture_offset: mix=%d, miy=%d, miz=%d\n", x, y, z);
    jade_mesh_set_texture_offset (model->mesh, x, y, z);
}

void jade_model_md2_set_texture_id (jade_model_md2_t *model, GLint textureId) {

    jade_mesh_set_texture_id (model->mesh, textureId);

    return;

}

void jade_model_md2_set_scale (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    jade_mesh_set_scale (model->mesh, x, y, z);

}

void jade_model_md2_set_position (jade_model_md2_t *model, GLfloat x, GLfloat y, GLfloat z) {

    jade_mesh_set_position (model->mesh, x, y, z);

}




///////////////////////////////////////////////////////////////////////////////
/// jade plane API

jade_plane_t *jade_plane_create_test () {

    int x = 0, z = 0;
    int width = 0, height = 0, i = 0;
    float scale = 0.0f;
    float *vertices = NULL;
    int vt_idx = 0;
    jade_plane_t *plane = NULL;

    plane = (jade_plane_t *)malloc (sizeof (jade_plane_t));
    if (plane == NULL)
        return NULL;

    plane->mesh = jade_mesh_create ();
/*
    //triangle data
    GLfloat vertices[9] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        0.0f, 1.0f, 0.0f
    };*/
    GLubyte colors[12] = {
        255, 0, 0, 0,
        0, 255, 0, 0,
        0, 0, 255 ,0
    };


    width = 4;
    height = 4;
    scale = 12.0f;

    vertices = (float *) malloc(width * height * 18 * sizeof(float));

    for (x = 0; x < width; x++) {
        for (z = 0; z < height; z++) {
            
            //triangle 1 first point 
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;

            //second point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;
                
            //third point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

            //triangle 1 first point
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;

            //second point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

            //third point
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

        }
    }

    
        

/*
    //set data to model
    jade_mesh_set_vertices (plane->mesh, vertices, 9 * sizeof(float));
    jade_mesh_set_colors (plane->mesh, colors, 12 * sizeof(GLubyte));
    jade_mesh_set_triangle_num (plane->mesh, 1);
*/
    jade_mesh_set_vertices (plane->mesh, vertices, width * height * 18 * sizeof(float));
    jade_mesh_set_colors (plane->mesh, colors, 12 * sizeof(GLubyte));
    jade_mesh_set_triangle_num (plane->mesh, width * height * 2);

    FREEANDNULL(vertices);
 
    return plane;

}



jade_plane_t *jade_plane_create (int width, int height, float scale) {

    jade_plane_t *plane = NULL;
    int vt_idx = 0, nm_idx = 0, uv_idx = 0;
    float *vertices = NULL;
    float *normals = NULL;
    float *uvs = NULL;
    int x = 0, z = 0;

#ifdef  FLOAT_ARG_FAIL
    scale = 12.0f;
#endif

    LOGD ("jade_plane_create: width = %d, height = %d, scale = %f.\n", width , height, scale);

    plane = (jade_plane_t *)malloc (sizeof (jade_plane_t));
    if (plane == NULL)
        return NULL;

    plane->mesh = jade_mesh_create ();

    vertices = (float *) malloc(width * height * 18 * sizeof(float));
    normals = (float *) malloc(width * height * 18 * sizeof(float));
    uvs = (float *) malloc(width * height * 12 * sizeof(float));

    /* Todo check vertices, normals, usv */
    //...

    for (x = 0; x < width; x++) {
        for (z = 0; z < height; z++) {

            //triangle 1 first point 
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;


            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;
               
            uvs[uv_idx++] = 0.0f;
            uvs[uv_idx++] = 0.0f;

            //second point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;
                
            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;

            uvs[uv_idx++] = 1.0f;
            uvs[uv_idx++] = 0.0f;

            //third point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;

            uvs[uv_idx++] = 1.0f;
            uvs[uv_idx++] = 1.0f;


            //triangle 1 first point
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = z * scale;

            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;

            uvs[uv_idx++] = 0.0f;
            uvs[uv_idx++] = 0.0f;

            //second point
            vertices[vt_idx++] = (x + 1) * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;

            uvs[uv_idx++] = 1.0f;
            uvs[uv_idx++] = 1.0f;

            //third point
            vertices[vt_idx++] = x * scale;
            vertices[vt_idx++] = 0.0f;
            vertices[vt_idx++] = (z + 1) * scale;

            normals[nm_idx++] = 0.0f;
            normals[nm_idx++] = 1.0f;
            normals[nm_idx++] = 0.0f;

            uvs[uv_idx++] = 0.0f;
            uvs[uv_idx++] = 1.0f;


        }
    }

    //set data to model
    jade_mesh_set_vertices (plane->mesh, vertices, width * height * 18 * sizeof(float));
    jade_mesh_set_normals (plane->mesh, normals, width * height * 18 * sizeof(float));
    jade_mesh_set_uvs (plane->mesh, uvs, width * height * 12 * sizeof(float));
    jade_mesh_set_triangle_num (plane->mesh, width * height * 2);

    FREEANDNULL(vertices);
    FREEANDNULL(uvs);
    FREEANDNULL(normals);
  

    return plane;

}

void jade_plane_destroy (jade_plane_t *plane) {

    if (plane->mesh != NULL)
        jade_mesh_destroy (plane->mesh);

    FREEANDNULL (plane);

    return;
}


void jade_plane_set_texture_id (jade_plane_t *plane, GLint textureId) {

    jade_mesh_set_texture_id (plane->mesh, textureId);

    return;
}

void jade_plane_set_position (jade_plane_t *plane, GLfloat x, GLfloat y, GLfloat z) {

    LOGD ("jade_plane_set_position: x = %f, y = %f, z = %f.\n", x, y, z);

#ifdef FLOAT_ARG_FAIL
    jade_mesh_set_position (plane->mesh, -10.0f, -2.0f, -3.0f);
#else
    jade_mesh_set_position (plane->mesh, x, y, z);
#endif    

    return;
}

void jade_plane_render (jade_plane_t *plane) {

    if (plane->mesh != NULL)
        jade_mesh_render (plane->mesh);


}

///////////////////////////////////////////////////////////////////////////////

void test_float (float f, float g, float h, float i) {


    LOGD ("**************** f = %f.\n", f);
    LOGD ("**************** g = %f.\n", g);
    LOGD ("**************** h = %f.\n", h);
    LOGD ("**************** i = %f.\n", i);

}
